#region license
/*
 * Copyright (C) 2007 CchenSoft.com
 * 
 * This library is free software; you can redistribute it and/or modify it 
 * under the terms of the GNU Lesser General Public License 2.1 or later, as
 * published by the Free Software Foundation. See the included License.txt
 * or http://www.gnu.org/copyleft/lesser.html for details.
 * 
 * Author: Billy Zhang
 */
#endregion

using System.Collections;
using System.IO;
using System.Xml;
using System;
using System.Text;
using System.Collections.Generic;

namespace CchenSoft.Workflow.Loader
{
    public class ActionDescriptor : AbstractDescriptor, IValidatable
    {
        //~ Instance fields ////////////////////////////////////////////////////////

        protected List<ConditionalResultDescriptor> conditionalResults = new List<ConditionalResultDescriptor>();
        protected List<FunctionDescriptor> postFunctions = new List<FunctionDescriptor>();
        protected List<FunctionDescriptor> preFunctions = new List<FunctionDescriptor>();
        protected List<ValidatorDescriptor> validators = new List<ValidatorDescriptor>();
        protected IDictionary metaAttributes = new Hashtable();
        protected RestrictionDescriptor restriction;
        protected ResultDescriptor unconditionalResult;
        protected string name;
        protected string view;
        protected bool autoExecute = false;
        protected bool common;
        protected bool finish = false;

        //~ Constructors ///////////////////////////////////////////////////////////

        /**
         * @deprecated use {@link DescriptorFactory} instead
         */
        public ActionDescriptor()
        {
        }

        /**
         * @deprecated use {@link DescriptorFactory} instead
         */
        public ActionDescriptor(XmlElement action)
        {
            Init(action);
        }

        //~ Methods ////////////////////////////////////////////////////////////////

        public bool AutoExecute
        {
            set { autoExecute = value; }
            get { return autoExecute; }
        }

        public bool IsCommon
        {
            get { return common; }
            set { common = value; }
        }

        public IList<ConditionalResultDescriptor> ConditionalResults
        {
            get { return conditionalResults; }
        }

        public bool Finish
        {
            set { finish = value; }
            get { return finish; }
        }

        public IDictionary MetaAttributes
        {
            set { metaAttributes = value; }
            get { return metaAttributes; }
        }

        public string Name
        {
            set { name = value; }
            get { return name; }
        }

        public IList<FunctionDescriptor> PostFunctions
        {
            get { return postFunctions; }
        }

        public IList<FunctionDescriptor> PreFunctions
        {
            get { return preFunctions; }
        }

        public RestrictionDescriptor Restriction
        {
            set { restriction = value; }
            get { return restriction; }
        }

        public ResultDescriptor UnconditionalResult
        {
            set { unconditionalResult = value; }
            get { return unconditionalResult; }
        }

        public IList<ValidatorDescriptor> Validators
        {
            get { return validators; }
        }

        public string View
        {
            set { view = value; }
            get { return view; }
        }

        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();

            if (name != null)
            {
                sb.Append(name);
            }

            if ((view != null) && (view.Length > 0))
            {
                sb.Append(" (").Append(view).Append(")");
            }

            return sb.ToString();
        }

        public void Validate()
        {
            ValidationHelper.Validate((ICollection<IValidatable>)preFunctions);
            ValidationHelper.Validate((ICollection<IValidatable>)postFunctions);
            ValidationHelper.Validate((ICollection<IValidatable>)validators);
            ValidationHelper.Validate((ICollection<IValidatable>)conditionalResults);

            if ((conditionalResults.Count > 0) && (unconditionalResult == null))
            {
                throw new InvalidWorkflowDescriptorException("Action " + name + " has conditional results but no fallback unconditional result");
            }

            if (restriction != null)
            {
                restriction.Validate();
            }

            if (unconditionalResult != null)
            {
                unconditionalResult.Validate();
            }
        }

        public override void WriteXML(XmlTextWriter writer, int indent)
        {
            //XMLUtil.printIndent(out, indent++);

            writer.WriteStartElement("action");

            writer.WriteAttributeString("id", Id.ToString());
            writer.WriteAttributeString("name", XMLUtil.Encode(name));
            writer.WriteAttributeString("view", XMLUtil.Encode(view));
            writer.WriteAttributeString("finish", "true");
            writer.WriteAttributeString("auto", "true");

            foreach (object key in metaAttributes.Keys)
            {
                //XMLUtil.printIndent(out, indent);
                writer.WriteStartElement("meta");
                writer.WriteAttributeString("name", XMLUtil.Encode(key));
                writer.WriteString(XMLUtil.Encode(metaAttributes[key]));
                writer.WriteEndElement();
            }

            if (restriction != null)
            {
                restriction.WriteXML(writer, indent);
            }

            if (validators.Count > 0)
            {
                //XMLUtil.printIndent(out, indent++);
                writer.WriteStartElement("validators");

                for (int i = 0; i < validators.Count; i++)
                {
                    ValidatorDescriptor validator = (ValidatorDescriptor)validators[i];
                    validator.WriteXML(writer, indent);
                }

                //XMLUtil.printIndent(out, --indent);
                writer.WriteEndElement();
            }

            if (preFunctions.Count > 0)
            {
                //XMLUtil.printIndent(out, indent++);
                writer.WriteStartElement("pre-functions");

                for (int i = 0; i < preFunctions.Count; i++)
                {
                    FunctionDescriptor function = (FunctionDescriptor)preFunctions[i];
                    function.WriteXML(writer, indent);
                }

                //XMLUtil.printIndent(out, --indent);
                writer.WriteEndElement();
            }

            //XMLUtil.printIndent(out, indent++);
            writer.WriteStartElement("results");

            for (int i = 0; i < conditionalResults.Count; i++)
            {
                ConditionalResultDescriptor result = (ConditionalResultDescriptor)conditionalResults[i];
                result.WriteXML(writer, indent);
            }

            if (unconditionalResult != null)
            {
                unconditionalResult.WriteXML(writer, indent);
            }

            //XMLUtil.printIndent(out, --indent);
            writer.WriteEndElement(); // "</results>";

            if (postFunctions.Count > 0)
            {
                //XMLUtil.printIndent(out, indent++);
                writer.WriteStartElement("post-functions");

                for (int i = 0; i < postFunctions.Count; i++)
                {
                    FunctionDescriptor function = (FunctionDescriptor)postFunctions[i];
                    function.WriteXML(writer, indent);
                }

                //XMLUtil.printIndent(out, --indent);
                writer.WriteEndElement();
            }

            //XMLUtil.printIndent(out, --indent);
            writer.WriteEndElement(); // "</action>";
        }

        protected void Init(XmlElement action)
        {
            string id = XMLUtil.GetAttributeValue(action, "id");
            try
            {

                Id = (Convert.ToInt32(id));
            }
            catch (Exception ex)
            {
                throw new ArgumentException("Invalid action id value '" + id + "'");
            }

            this.name = XMLUtil.GetAttributeValue(action, "name");
            this.view = XMLUtil.GetAttributeValue(action, "view");
            this.autoExecute = "true".Equals(XMLUtil.GetAttributeValue(action, "auto").ToLower());
            this.finish = "true".Equals(XMLUtil.GetAttributeValue(action, "finish").ToLower());

            XmlNodeList metas = action.SelectNodes("meta");

            for (int i = 0; i < metas.Count; i++)
            {
                XmlElement child = (XmlElement)metas[i];

                string name = XMLUtil.GetAttributeValue(child, "name");
                this.metaAttributes.Add(name, child.InnerText);
            }

            // set up validators - OPTIONAL
            XmlNode v = action.SelectSingleNode("validators");

            if (v != null)
            {
                XmlNodeList validators = v.SelectNodes("validator");

                for (int k = 0; k < validators.Count; k++)
                {
                    XmlElement validator = (XmlElement)validators[k];
                    ValidatorDescriptor validatorDescriptor = DescriptorFactory.GetFactory().CreateValidatorDescriptor(validator);
                    validatorDescriptor.Parent = this;
                    this.validators.Add(validatorDescriptor);
                }
            }

            // set up pre-functions - OPTIONAL
            XmlNode pre = action.SelectSingleNode("pre-functions");

            if (pre != null)
            {
                XmlNodeList preFunctions = pre.SelectNodes("function");

                for (int k = 0; k < preFunctions.Count; k++)
                {
                    XmlElement preFunction = (XmlElement)preFunctions[k];
                    FunctionDescriptor functionDescriptor = DescriptorFactory.GetFactory().CreateFunctionDescriptor(preFunction);
                    functionDescriptor.Parent = this;
                    this.preFunctions.Add(functionDescriptor);
                }
            }

            // set up results - REQUIRED
            XmlNode resultsElement = action.SelectSingleNode("results");
            XmlNodeList results = resultsElement.SelectNodes("result");

            for (int k = 0; k < results.Count; k++)
            {
                XmlElement result = (XmlElement)results[k];
                ConditionalResultDescriptor conditionalResultDescriptor = new ConditionalResultDescriptor(result);
                conditionalResultDescriptor.Parent = this;
                this.conditionalResults.Add(conditionalResultDescriptor);
            }

            XmlElement unconditionalResult = (XmlElement)resultsElement.SelectSingleNode("unconditional-result");

            // [KAP] This allows loading a workflow with actions without unconditional-results
            if (unconditionalResult != null)
            {
                this.unconditionalResult = DescriptorFactory.GetFactory().CreateResultDescriptor(unconditionalResult);
                this.unconditionalResult.Parent = this;
            }

            // set up post-functions - OPTIONAL
            XmlNode post = action.SelectSingleNode("post-functions");

            if (post != null)
            {
                XmlNodeList postFunctions = post.SelectNodes("function");

                for (int k = 0; k < postFunctions.Count; k++)
                {
                    XmlElement postFunction = (XmlElement)postFunctions[k];
                    FunctionDescriptor functionDescriptor = DescriptorFactory.GetFactory().CreateFunctionDescriptor(postFunction);
                    functionDescriptor.Parent = this;
                    this.postFunctions.Add(functionDescriptor);
                }
            }

            // set up restrict-to - OPTIONAL
            XmlElement restrictElement = (XmlElement)action.SelectSingleNode("restrict-to");

            if (restrictElement != null)
            {
                restriction = new RestrictionDescriptor(restrictElement);

                if (restriction.ConditionsDescriptor == null)
                {
                    restriction = null;
                }
                else
                {
                    restriction.Parent = this;
                }
            }
        }

    }
}
